package com.corejsf;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.sql.DataSource;

/**
 * Configures your user, credentials, and keeps the users in a List.
 *
 * @author Vukasin Simic
 * @version 1.0
 */

@Named("usermanager")
@ApplicationScoped
public class UserManager implements Serializable {
	
	/**
	 * DataSource for JBoss AS 7.1 connection pool.
	 */
    @Resource(mappedName = "java:jboss/datasources/vvdb")
    private DataSource dataSource;

    /**
     * Serial Version ID.
     */
    private static final long serialVersionUID = -5129814544398510305L;

    /**
     * Injecting configurations.
     */
    @Inject private Configurations configs;

    /**
     * Injecting credentials.
     */
    @Inject private Credentials credentials;

    /**
     * List of created users.
     */
    private static List<User> users = new ArrayList<User>();

    /**
     * The count of how many employees have existed in the user list.
     *
     * Used to generate employee numbers.
     */
    private int employeeCount = 0;

    /**
     * No parameter constructor.
     */
    public UserManager() {
    }

    /**
     * Adding initial superuser and adds some non admin users.
     */
    @PostConstruct
    public void loadUsers() {
        Connection connection = null;
        Statement stmt = null;
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection.createStatement();
                    ResultSet result = stmt.executeQuery(
                            "SELECT * FROM Users");
                    while (result.next()) {
                    	users.add(new User(
                                result.getString("username"),
                                result.getString("lastname"),
                                result.getString("firstname"),
                                result.getString("password"),
                                result.getBoolean("admin"),
                                result.getInt("employeenumber")));
                    }
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }

                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in loadUsers");
            ex.printStackTrace();
        }
    }

    /**
     * Passes username and password to be used by user validation method.
     *
     * @param username to be authenticated
     * @param password to be authenticated
     * @return user or null.
     */
    public User login(final String username, final String password) {
        User user = usernameExists(users, username);
        if (user != null) {
            if (user.getPassword().equals(password)) {
                return user;
            }
        }
        return null;
    }

    /**
     * Checks if the username exists in the list of users.
     *
     * @param list of existing user accounts
     * @param username to be checked for uniqueness among existing user accounts
     * @return User or null
     */
    public User usernameExists(final List<User> list, final String username) {
        for (User user: users) {
            if (user.getUserName().equals(username)) {
                return user;
            }
        }
        return null;
    }

    /**
     * Adds a user from the add user button.
     *
     * @return navigation outcome string
     */
    public String addUser() {
        String proposedUsername = credentials.getUsername();
        
        if (usernameExists(users, proposedUsername) == null) {
            Connection connection = null;
            PreparedStatement stmt = null;
            employeeCount++;
            try {
                try {
                    connection = dataSource.getConnection();
                    try {
                        stmt = connection.prepareStatement("INSERT INTO Users VALUES (?,?,?,?,?,?)");
                        stmt.setString(1, credentials.getUsername());
                        stmt.setString(2, credentials.getLastName());
                        stmt.setString(3, credentials.getFirstName());
                        stmt.setString(4, credentials.getPassword());
                        stmt.setBoolean(5, credentials.isAdmin());
                        stmt.setInt(6, employeeCount);
                        stmt.executeUpdate();
                        
                        users.add(new User(credentials.getUsername(),
                            credentials.getLastName(), credentials.getFirstName(),
                            credentials.getPassword(),
                            credentials.isAdmin(), employeeCount));
                    } finally {
                        if (stmt != null) {
                            stmt.close();
                        }
                    }
                } finally {
                    if (connection != null) {
                        connection.close();
                    }
                }
            } catch (SQLException ex) {
            	employeeCount--;
                System.out.println("Error in addUser()");
                ex.printStackTrace();
            }
            return "success";
        }
        return "failure";
    }

    /**
     * Deleting a user using the button in the UI.
     *
     * @param userToDelete from existing user accounts
     * @return null in lieu of navigation outcome string to prompt refresh
     */
    public String deleteUser(final User userToDelete) {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection.prepareStatement("DELETE FROM Users WHERE Username = ?");
                    stmt.setString(1, userToDelete.getUserName());
                    stmt.executeUpdate();
                    users.remove(userToDelete);
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }
                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in deleteUser()");
            ex.printStackTrace();
        }
        return null;
     }

    /**
     * Gets credentials.
     *
     * @return credentials.
     */
    public Credentials getCredentials() {
        return credentials;
    }

    /**
     * Sets a user's credentials.
     *
     * @param credentials entered by user into form
     */
    public void setCredentials(final Credentials credentials) {
        this.credentials = credentials;
    }

    /**
     * Gets a list of users.
     *
     * @return users
     */
    public List<User> getUsers() {
        return users;
    }

    /**
     * Saves the changes made after editing a user.
     *
     * @return null in lieu of navigation outcome string to prompt refresh
     */
    public String save() {
        for (User user : users){
        	if(user.isEditable()){
        		updateUser(user);
        		user.setEditable(false);
        	}
            
        }
        return null;
    }
    
    /**
     * Updating a user.
     *
     * @param userToUpdate from existing user accounts
     * @return null in lieu of navigation outcome string to prompt refresh
     */
    private void updateUser(User userToUpdate) {
        Connection connection = null;
        PreparedStatement stmt = null;
        try {
            try {
                connection = dataSource.getConnection();
                try {
                    stmt = connection.prepareStatement("UPDATE Users SET Username = ?, LastName = ?, FirstName = ?," +
                    		"Password = ?, Admin = ? WHERE EmployeeNumber = ?");
                    stmt.setString(1, userToUpdate.getUserName());
                    stmt.setString(2, userToUpdate.getLastName());
                    stmt.setString(3, userToUpdate.getFirstName());
                    stmt.setString(4, userToUpdate.getPassword());
                    stmt.setBoolean(5, userToUpdate.isAdmin());
                    stmt.setInt(6, userToUpdate.getEmployeeNumber());
                    stmt.executeUpdate();
                } finally {
                    if (stmt != null) {
                        stmt.close();
                    }
                }
            } finally {
                if (connection != null) {
                    connection.close();
                }
            }
        } catch (SQLException ex) {
            System.out.println("Error in updateUser()");
            ex.printStackTrace();
        }
     }
    
}
